package esb.chapter9.restaurant.routing.camel;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.servicemix.camel.JbiExchange;
import org.apache.servicemix.camel.JbiMessage;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

import esb.chapter9.restaurant.routing.camel.exception.ExpirationException;
import esb.chapter9.restaurant.routing.dao.ReservationDAOIF;
import esb.chapter9.restaurant.routing.model.Reservation;

public class ExpirationCheckProcessor implements Processor {
	
	public void process(Exchange exchange) throws Exception {
		String payload = getPayload(exchange);
		Reservation inputReservation = getReservation(payload);
		ReservationDAOIF reservationDAO = exchange.getContext().getRegistry().lookup("restaurantDAO", ReservationDAOIF.class);
		Reservation reservation = reservationDAO.getReservation(inputReservation.getReservationID());
		JbiExchange jbiExchange = (JbiExchange) exchange;
		JbiMessage message = new JbiMessage();
		message.setHeaders(jbiExchange.getIn().getHeaders());
		if(reservation == null || reservation.getReservationID() == null) {
			exchange.setException(new ExpirationException(
					"reservation could not be found for ID " + inputReservation.getReservationID()));
		} else {
			boolean timeout = isTimeout(reservation);
			System.out.println("timeout " + timeout);
			if (timeout) {
				System.out.println("reservation has timed out " + inputReservation.getReservationID());
				message.setBody("<confirmation><invalid>true</invalid><restaurant>" + 
						reservation.getRestaurant().getName() + "</restaurant></confirmation>");
			} else {
				message.setBody(makeConfirmationMessage(reservation));
			}
		}
		jbiExchange.setIn(message);
    }
	
	private Reservation getReservation(String payload) throws Exception {
		XPath xpath = XPathFactory.newInstance().newXPath();
        InputSource payloadSource = new InputSource(new StringReader(payload));
        XPathExpression pathExpr = xpath.compile("//restaurant");
        Node restaurantNode = (Node) pathExpr.evaluate(payloadSource, XPathConstants.NODE);
        String reservationID = restaurantNode.getAttributes().getNamedItem("reservationID").getNodeValue();
        Reservation reservation = new Reservation();
        reservation.setReservationID(Long.valueOf(reservationID));
        reservation.setRestaurantName(restaurantNode.getTextContent());
        return reservation;
	}
	
	private String makeConfirmationMessage(Reservation reservation) {
		return "<confirmation>" +
				"<restaurant>" + reservation.getRestaurant().getName() + "</restaurant>" +
				"<guestname>" + reservation.getGuestName() + "</guestname>" +
				"<roomnumber>" + reservation.getRoomNumber() + "</roomnumber>" +
				"</confirmation>";
	}
	
	private String getPayload(Exchange exchange) throws Exception {
		Object bodyObj = exchange.getIn().getBody();
		String payload = null;
		if(bodyObj instanceof StreamSource) {
			StreamSource payloadStream =  (StreamSource) exchange.getIn().getBody();
			payload = convertToString(payloadStream.getInputStream());
		} else if (bodyObj instanceof DOMSource) {
			ByteArrayOutputStream bOut = new ByteArrayOutputStream();
			StreamResult result = new StreamResult(bOut);
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
			transformer.transform((Source) bodyObj, result);
			payload = new String(bOut.toByteArray());
		} else {
			payload = (String) bodyObj;
		}
		System.out.println("payload " + payload);
		return payload;
	}
	
	private boolean isTimeout(Reservation reservation) {
		long currentTime = System.currentTimeMillis();
		long timeoutValue = currentTime - reservation.getCurrentTime().getTime();
		if(timeoutValue > reservation.getRestaurant().getTimeout())
			return true;
		else
			return false;
		
	}
	
	private String convertToString(InputStream inputStream) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
		StringBuilder sb = new StringBuilder();
		String line = null;
		while ((line = br.readLine()) != null) {
			sb.append(line);
		}
		br.close();
		return sb.toString();

	}

}
